/**
 * 目前只支持单行单列模式。
 */
import { _decorator, Component, Node, ScrollView, NodePool, error, instantiate, Widget, Size, warn, Vec2, v2, UITransform, Enum } from 'cc';
import { DEBUG, EDITOR } from 'cc/env';

const { ccclass, property } = _decorator;

enum ScrollModel { Horizontal = 0, Vertical = 1 }
// enum ScrollType{ Single= 0, Multiple= 1 };

export interface tvCell extends Node {
	// 当前实际的下标
	tvIndex: number;
	// 将要转变的下标(用于增删操作)
	_tvIndex: number;
}
export interface cellAgent{
	getSize?(index: number, data?: any): number ;
	uninitCell?(cell: tvCell);
	reloadCell?(cell: tvCell, data?: any);
	initCell(cell: tvCell, data?: any, tv?: TableView);
}

@ccclass
export default class TableView extends ScrollView {
	// 重写
	@property({ visible: false, override: true }) horizontal = false;

	@property({ visible: false, override: true }) vertical = true;

	@property({ type: Component.EventHandler, visible: false, override: true }) scrollEvents = [];

	// 新增
	@property(Node) _cell: Node = null;

	@property({ type: Node, tooltip: '渲染节点' })
	get cell() { return this._cell; }

	set cell(value) {
		this.clear();
		this._cellPool.clear();
		this._cell = value;
	}

	@property({ type: Enum(ScrollModel), tooltip: '滑动方向' })
	get scrollModel() { return this.horizontal ? ScrollModel.Horizontal : ScrollModel.Vertical; }

	set scrollModel(value) {
		if (!EDITOR) {
			error('[tableView] 不允许动态修改scrollModel');

			return;
		}

		if (value === ScrollModel.Horizontal) {
			this.horizontal = true;
			this.vertical = false;
		} else {
			this.horizontal = false;
			this.vertical = true;
		}
	}

	// @property({ type: ScrollType, tooltip: 'Single: 单行/单列\nMultiple: 多行/多列' })
	// scrollType: number = ScrollType.Single;

	// @property({ visible: function () { return this.scrollType == ScrollType.Multiple }, tooltip: 'Horizontal: 有几行\Vertical: 有几列' })
	// mulCount: number = 0;

	private _cellPool: NodePool = new NodePool('viewCell');

	private _cellData = null;

	private _cellCount = 0;

	private _childCount = 0;

	private _startIndex = 0;

	private _maxStartIndex = 0;

	private _endIndex = 0;

	private _anchorCenterX = 0;

	private _anchorCenterY = 0;

	private _anchorLeftX = 0;

	private _anchorTopY = 0;

	private _cellAnchorX = 0;

	private _cellAnchorY = 0;

	private _customSize = false;

	private _cellAccumSizes: number[] = [];

	private _cellSizes: number[] = [];

	private _cellSize: number = 0;

	private _updateRefreshOnce = false;

	private _updateRefreshForce = false;

	private _updateCellsOn = 0;

	private _updateCellsOnce = false;
	private _cellAgent: cellAgent = null;

	onDestroy() {
		while (this._cellPool.size()) {
			this._cellPool.get().destroy();
		}

		if (super.onDestroy) {
			super.onDestroy();
		}
	}

	onEnable() {
		this.node.on(ScrollView.EventType.SCROLL_BEGAN, this._onScrollBegin, this);
		this.node.on(ScrollView.EventType.SCROLL_ENDED, this._onScrollEnd, this);

		if (super.onEnable) {
			super.onEnable();
		}
	}

	onDisable() {
		this.node.off(ScrollView.EventType.SCROLL_BEGAN, this._onScrollBegin, this);
		this.node.off(ScrollView.EventType.SCROLL_ENDED, this._onScrollEnd, this);

		if (super.onDisable) {
			super.onDisable();
		}
	}

	private _onScrollBegin() {
		this._updateCellsOn = 2;
	}

	private _onScrollEnd(view) {
		// onScrollEnd 执行了二次，希望再第二次才执行
		this._updateCellsOn--;
		this._updateCellsOnce = true;
	}

	stopAutoScroll() {
		this._updateCellsOn = 0;
		if (!this._updateCellsOnce && this._updateCellsOn > 0) {
			this._updateCellsOnce = true;
		}

		super.stopAutoScroll();
	}

	getCellCount() {
		return this._cellCount;
	}

	setCellCount(num?: number) {
		if (typeof num === 'number' && num >= 0) {
			this._cellCount = num;
		} else if (this._cellCount != this._cellData.length) {
			this._cellCount = this._cellData.length;
			this._updateCellCount();
			// error('[tableView] setCellCount 参数错误');
		}
	}

	getCellData() {
		return this._cellData;
	}

	setCellData(data: any) {
		if (typeof data !== 'undefined') {
			this._cellData = data;
		}
	}

	getCellRange() {
		const children: tvCell[] = this.content.children as tvCell[];

		if (children.length == 0) {
			return { min: -1, max: -1 };
		}

		return { min: children[0].tvIndex, max: children[children.length - 1].tvIndex };
	}

	/**
	 * [实时操作] 如果动态修改scrollBar的大小了，则需要调用此方法更新
	 */
	refreshScrollBar() {
		// @ts-ignore
		this._updateScrollBar(this._getHowMuchOutOfBoundary());
	}

	/**
	 * [延时操作] 刷新
	 * 1、cell大小改变需要刷新cell位置时调用
	 * 2、content大小、锚点发生改变后，需要刷新cell位置时调用
	 * 3、cellCount改变，需要刷新cell时调用
	 * 
	 * 调用refresh并不代表会执行cell的init或uninit方法
	 * 只有当cell的index因插入、删除、滚屏、初始化等原因需要发生变化时，才会触发init或uninit方法
	 */
	refresh(force = true) {
		this._updateRefreshOnce = true;
		if (force) {
			this._updateRefreshForce = true;
		}
	}

	/**
	 * [实时操作] 刷新
	 */
	refreshSync(force = true) {
		this._stopRefresh();
		this._initData();
		this._updateCells(force);
	}

	/**
	 * 停止refresh
	 */
	private _stopRefresh() {
		this._updateRefreshOnce = false;
		this._updateRefreshForce = false;
	}

	/**
	 * [实时操作] 初始化
	 */
	init(cella:cellAgent,data: any[]) {
		if (DEBUG) {
			if (!this.content) {
				return error('[tableView] 请指定content');
			}

			if (!this.cell) {
				return error('[tableView] 请指定cell');
			}
		}

		this.clear();
		this._cellAgent = cella;
		this.setCellData(data || null);
		this.setCellCount(data.length);

		this._initData();

		this.stopAutoScroll();
		this.scrollToOrigin();

		this._updateCells(true);
	}

	/**
	 * [实时操作] 清空
	 */
	clear() {
		this._cellCount = 0;
		this._childCount = 0;
		this._startIndex = 0;
		this._endIndex = 0;
		this._maxStartIndex = 0;
		this.content.getComponent(UITransform)[this.horizontal ? 'width' : 'height'] = 0;

		this._stopRefresh();
		this._updateCellCount();
	}

	/**
	 * [实时操作] 重载
	 */
	reload(start = 0, num?: number) {
		if (typeof num === 'undefined') {
			this.content.children.forEach((cell: tvCell) => {
				if (cell.tvIndex >= start) {
					this._reloadCell(cell);
				}
			});
		} else {
			if (num <= 0) return;
			if (start < 0) start = start + this._cellCount;
			if (start + num < 0) return;

			this.content.children.forEach((cell: tvCell) => {
				if (cell.tvIndex >= start && cell.tvIndex < start + num) {
					this._reloadCell(cell);
				}
			})
		}
	}

	/**
	 * [延时操作] 插入
	 */
	insert(start = -1, num = 1, data?: any) {
		if (num <= 0) return;
		if (start < 0) start = start + this._cellCount + 1;
		if (start < 0) start = 0;

		this.setCellData(data);

		const children = this.content.children;

		for (let index = children.length - 1; index >= 0; index--) {
			const node: tvCell = children[index] as tvCell;

			if (node._tvIndex >= start) {
				node._tvIndex += num;
			}
		}

		this.setCellCount(this._cellCount + num);
		this.refresh();
	}

	/**
	 * [延时操作] 删除
	 */
	remove(start = -1, num = 1, data?: any) {
		if (num <= 0) return;
		if (start < 0) start = start + this._cellCount;
		let end = start + num;

		if (start < 0) {
			start = 0;
		}

		if (end > this._cellCount) {
			end = this._cellCount;
		}

		num = end - start;
		if (start >= this._cellCount || end <= 0 || num < 0 || start >= end) return;
		this.setCellData(data);

		const children = this.content.children;

		for (let index = children.length - 1; index >= 0; index--) {
			const node: tvCell = children[index] as tvCell;

			if (node._tvIndex >= start) {
				if (node._tvIndex < end) {
					node._tvIndex = -1;
				} else {
					node._tvIndex -= num;
				}
			}
		}

		this.setCellCount(this._cellCount - num);
		this.refresh();
	}

	private _getCell(): tvCell {
		let node: tvCell = null;

		if (this._cellPool.size()) {
			node = this._cellPool.get() as tvCell;
		} else {
			node = instantiate(this.cell) as tvCell;
		}

		node.tvIndex = -1;
		node._tvIndex = -1;

		return node;
	}

	private _putCell(node: tvCell) {
		this._cellPool.put(node);
	}

	private _initCell(cell: tvCell, index: number) {
		if (index >= 0) {
			if (cell.tvIndex != index || cell.tvIndex != cell._tvIndex) {
				if (cell.tvIndex >= 0) 
					this._cellAgent.uninitCell?.call(this._cellAgent,cell);
				cell.tvIndex = index;
				this._cellAgent.initCell(cell, this._cellData[index], this);
			}
			cell.tvIndex = index;
			cell._tvIndex = index;
		}
	}

	private _uninitCell(cell: tvCell) {
		if (cell.tvIndex >= 0) {
			this._cellAgent.uninitCell?.call(this._cellAgent,cell);
			cell.tvIndex = -1;
			cell._tvIndex = -1;
		}
	}

	private _reloadCell(cell: tvCell) {
		this._cellAgent?.reloadCell.call(this._cellAgent, cell.tvIndex, this._cellData);
	}

	/**
	 * 获取默认cell大小
	 */
	private _getDefaultCellSize() {
		if (this.cell) {
			if (DEBUG && this.cell.getComponent(Widget)) {
				warn('[tableView] cell根节点中存在Widget，可能无法正确获取Size');
			}

			return this.cell.getComponent(UITransform).contentSize;
		}

		return Size.ZERO;
	}

	/**
	 * 获取默认cell锚点
	 */
	private _getDefaultCellAnchor() {
		if (this.cell) {
			return this.cell.getComponent(UITransform).anchorPoint;
		}

		return Vec2.ZERO;
	}

	private _getScrollLengh() {
		const offset = this.getScrollOffset();
		const scrollLen = this.horizontal ? -offset.x : offset.y;

		if (scrollLen < 0) {
			return 0;
		}

		// 有maxStartIndex作为限制，这里可以不做限制
		// const maxOffset = this.getMaxScrollOffset();
		// const maxScrollLen = this.horizontal ? -maxOffset.x : maxOffset.y;
		// if (scrollLen > maxScrollLen) {
		//     return maxScrollLen;
		// }
		return scrollLen;
	}

	/**
	 * 初始化数值
	 */
	private _initData() {
		this.setCellCount();
		const defaultCellSize = this._getDefaultCellSize();
		const prop = this.horizontal ? 'width' : 'height';
		const viewLen = this.content.parent.getComponent(UITransform)[prop];

		// 是否自定义大小
		this._customSize = this._cellAgent.getSize!=null;

		// 初始默认数据
		this._cellSize = defaultCellSize[prop];
		this._cellAccumSizes.length = 0;
		this._cellSizes.length = 0;
		this._maxStartIndex = 0;
		this._startIndex = -1;
		this._endIndex = 0;

		// 填充cell数据
		if (this._customSize) {
			/**
			 * [性能问题] 这里有可能会出现性能问题, 原因可能有：
			 * 1、view.getSize的复杂度过高
			 * 2、数据量极大
			 */
			for (let index = 0, accumSize = 0, size = 0; index < this._cellCount; index++) {
				size = this._cellAgent.getSize(index, this._cellData);
				if (!size || size < 0) size = this._cellSize;
				this._cellSizes.push(size);

				accumSize += size;
				this._cellAccumSizes.push(accumSize);
			}
		}

		// 计算childCount
		if (this._customSize) {
			// 自定义cell大小时，childCount需要实时动态计算，这里计算一下滑到最底部时的childCount(因为后面正好用上)
			this._childCount = this._cellCount;

			const accumIndex = this._cellAccumSizes.length - 1;
			const accumSize = this._cellAccumSizes[accumIndex];

			for (let index = 1, size = 0; index <= accumIndex; index++) {
				size = this._cellAccumSizes[accumIndex - index];

				if (accumSize - size >= viewLen) {
					this._childCount = index + 1;
					break;
				}
			}
		} else {
			this._childCount = Math.ceil(viewLen / this._cellSize) + 1;
		}

		// 计算最大的开始下标
		if (this._cellCount > this._childCount) {
			this._maxStartIndex = this._cellCount - this._childCount;
		} else {
			this._maxStartIndex = 0;
		}

		// 计算content大小
		const trans = this.content.getComponent(UITransform);

		if (this._customSize) {
			trans[prop] = this._cellAccumSizes[this._cellAccumSizes.length - 1] || 0;
		} else {
			trans[prop] = this._cellSize * this._cellCount;
		}

		// 计算基础定位数值
		const cellAnchor = this._getDefaultCellAnchor();

		this._anchorCenterX = (0.5 - trans.anchorX) * trans.width;
		this._anchorCenterY = (0.5 - trans.anchorY) * trans.height;
		this._anchorLeftX = (0 - trans.anchorX) * trans.width;
		this._anchorTopY = (1 - trans.anchorY) * trans.height;
		this._cellAnchorX = cellAnchor.x;
		this._cellAnchorY = 1 - cellAnchor.y;
	}

	/**
	 * 更新cell的数量，不够添加，多了删除
	 */
	private _updateCellCount() {
		const children: tvCell[] = this.content.children as tvCell[];

		if (children.length == this._childCount) {
			return;
		} else if (children.length > this._childCount) {
			let cell: tvCell = null;

			// 优先删除即将要废弃的cell
			for (let index = children.length - 1; index >= this._childCount; index--) {
				cell = children[index];
				if (cell._tvIndex < this._startIndex || cell._tvIndex > this._endIndex) {
					this._uninitCell(cell);
					this._putCell(cell);
				}
			}

			// 从后往前删除
			for (let index = children.length - 1; index >= this._childCount; index--) {
				cell = children[index];
				this._uninitCell(cell);
				this._putCell(cell);
			}
		} else {
			for (let index = children.length; index < this._childCount; index++) {
				this.content.addChild(this._getCell())
			}
		}
	}

	/**
	 * 根据滑动距离，获得startIndex
	 */
	private _getStartIndex(scrollLen: number) {
		let startIndex = 0;
		const maxStartIndex = this._maxStartIndex;

		if (this._customSize) {
			const cellAccumSizes = this._cellAccumSizes;

			if (cellAccumSizes.length < 5) {
				// 普通循环
				for (; startIndex < maxStartIndex; startIndex++) {
					if (cellAccumSizes[startIndex] > scrollLen) {
						break;
					}
				}
			} else {
				// 二分查找
				let min = 0, max = maxStartIndex, value = 0;

				while (max >= min) {
					startIndex = Math.floor((max + min) / 2);
					value = cellAccumSizes[startIndex];

					if (scrollLen == value) {
						if (startIndex < maxStartIndex) startIndex += 1;
						break;
					} else if (scrollLen < value && (startIndex == 0 || scrollLen >= cellAccumSizes[startIndex - 1])) {
						break;
					} else if (scrollLen > value) {
						min = startIndex + 1;
					} else {
						max = startIndex - 1;
					}
				}
			}
		} else {
			startIndex = Math.floor(scrollLen / this._cellSize);
			if (startIndex < 0) { startIndex = 0 }
			else if (startIndex > maxStartIndex) { startIndex = maxStartIndex }
		}

		return startIndex;
	}

	/**
	 * 更新开始下标、结束下标、childNum(只有自定义cell大小才会更新)
	 */
	private _updateCellRange(): boolean {
		// 滚动距离
		const scrollLen = this._getScrollLengh();
		// 当前滚动距离对应的起始cell下标
		const startidx = this._getStartIndex(scrollLen);

		if (startidx == this._startIndex) {
			return false;
		}

		this._startIndex = startidx

		// 自定义cell大小需要更新childCount
		if (this._customSize) {
			const viewLen = this.content.parent.getComponent(UITransform)[this.horizontal ? 'width' : 'height'];
			const cellAccumLen = this._cellAccumSizes.length;

			// 这里小数比较大小的做法，会出现0.1+0.2!=0.3的情况么?
			if (this._cellAccumSizes[cellAccumLen - 1] - viewLen <= scrollLen) {
				this._childCount = cellAccumLen - this._startIndex;
			} else {
				const startSize = this._cellAccumSizes[this._startIndex];

				for (let endIndex = this._startIndex + 1, accumSize = 0; endIndex < cellAccumLen; endIndex++) {
					accumSize = this._cellAccumSizes[endIndex];

					// 这里小数比较大小的做法，会出现0.1+0.2!=0.3的情况么?
					if (accumSize - viewLen >= scrollLen) {
						if (accumSize - startSize >= viewLen) {
							this._childCount = endIndex - this._startIndex + 1;
						} else {
							this._childCount = endIndex - this._startIndex + 2;
						}

						break;
					}
				}
			}
		}

		// 当前滚动距离对应的终止cell下标
		this._endIndex = this._startIndex + this._childCount - 1;

		return true;
	}

	/**
	 * 更新cell状态
	 */
	private _updateCell(cell: tvCell, index?: number) {
		if (typeof index === 'number') {
			this._initCell(cell, index);
		} else {
			this._initCell(cell, cell._tvIndex);
			index = cell.tvIndex;
		}

		const pos = cell.getPosition();

		if (this.horizontal) {
			if (this._customSize) {
				pos.x = this._anchorLeftX - this._cellSizes[index] * this._cellAnchorX + this._cellAccumSizes[index];
			} else {
				pos.x = this._anchorLeftX - this._cellSize * this._cellAnchorX + this._cellSize * (index + 1);
			}

			pos.y = this._anchorCenterY;
		} else {
			if (this._customSize) {
				pos.y = this._anchorTopY + this._cellSizes[index] * this._cellAnchorY - this._cellAccumSizes[index];
			} else {
				pos.y = this._anchorTopY + this._cellSize * this._cellAnchorY - this._cellSize * (index + 1);
			}

			pos.x = this._anchorCenterX;
		}

		cell.setPosition(pos);
	}

	/**
	 * 更新cells状态
	 * 
	 * 根据startIndex和endIndex，将content下的节点分成keepCells和changeCells
	 * 1、统一更新keepCells和changeCells里cell的坐标
	 * 2、changeCells里的cell会进行init
	 */
	private _updateCells(force = false) {
		this._updateCellsOnce = false;
		if (!this._updateCellRange()) { return; }
		this._updateCellCount();
		if (!this._childCount) { return; }

		const startIndex = this._startIndex;
		const endIndex = this._endIndex;
		const children: tvCell[] = this.content.children as tvCell[];
		const keepCells: tvCell[] = [];
		const changeCells: tvCell[] = [];

		children.forEach((cell) => {
			if (cell._tvIndex < startIndex || cell._tvIndex > endIndex || cell._tvIndex != cell.tvIndex) {
				this._uninitCell(cell);
				changeCells.push(cell);
			} else {
				keepCells[cell.tvIndex] = cell;
			}
		});

		// 没有可刷新的
		if (changeCells.length == 0) {
			if (force) {
				children.forEach((cell) => {
					cell.tvIndex = -1;
					this._updateCell(cell);
				});
			}
		} else if (keepCells.length == 0) {
			// 全部cell都需要刷新
			children.forEach((cell, index) => this._updateCell(cell, startIndex + index));
		}
		// 只有部分cell需要刷新
		else {
			for (let index = startIndex, changePoint = 0, i = 0; index <= endIndex; index++, i++) {
				if (keepCells[index] != null) {
					this._updateCell(keepCells[index]);
				} else {
					this._updateCell(changeCells[changePoint++], index);
				}
			}
		}

		// 排序
		children.forEach(function (node) {
			node.setSiblingIndex(node.tvIndex - startIndex);
		})
	}

	getPerScrollOffset() {
		if (this.horizontal) {
			return v2(this.getScrollOffset().x / this.getMaxScrollOffset().x, 0);
		} else {
			return v2(0, this.getScrollOffset().y / this.getMaxScrollOffset().y);
		}
	}

	scrollToIndex(index?: number, timeInSecond?: number, attenuated?: boolean) {
		const off = v2(0, 0);

		if (this.horizontal) {
			off.x = index / this._cellCount;
		} else {
			off.y = index / this._cellCount;
		}

		this.scrollToPerOffset(off, timeInSecond, attenuated);
	}

	scrollToPerOffset(offset: Vec2, timeInSecond?: number, attenuated?: boolean) {
		if (this.horizontal) {
			offset.x *= this.getMaxScrollOffset().x;
		} else {
			offset.y *= this.getMaxScrollOffset().y;
		}

		this.scrollToOffset(offset, timeInSecond, attenuated);
	}

	scrollToOrigin(timeInSecond?: number, attenuated?: boolean) {
		if (this.horizontal) {
			this.scrollToLeft(timeInSecond, attenuated)
		} else {
			this.scrollToTop(timeInSecond, attenuated)
		}
	}

	scrollToBottom(timeInSecond?: number, attenuated?: boolean) {
		if (timeInSecond) {
			this._updateCellsOn = 1;
		} else {
			this._updateCellsOnce = true;
		}

		super.scrollToBottom(timeInSecond, attenuated);
	}

	scrollToTop(timeInSecond?: number, attenuated?: boolean) {
		if (timeInSecond) {
			this._updateCellsOn = 1;
		} else {
			this._updateCellsOnce = true;
		}

		super.scrollToTop(timeInSecond, attenuated);
	}

	scrollToLeft(timeInSecond?: number, attenuated?: boolean) {
		if (timeInSecond) {
			this._updateCellsOn = 1;
		} else {
			this._updateCellsOnce = true;
		}

		super.scrollToLeft(timeInSecond, attenuated);
	}

	scrollToRight(timeInSecond?: number, attenuated?: boolean) {
		if (timeInSecond) {
			this._updateCellsOn = 1;
		} else {
			this._updateCellsOnce = true;
		}

		super.scrollToRight(timeInSecond, attenuated);
	}

	scrollToOffset(offset: Vec2, timeInSecond?: number, attenuated?: boolean) {
		if (timeInSecond) {
			this._updateCellsOn = 1;
		} else {
			this._updateCellsOnce = true;
		}

		super.scrollToOffset(offset, timeInSecond, attenuated);
	}

	update(dt) {
		super.update(dt);
		if (this._updateRefreshOnce) this.refreshSync(this._updateRefreshForce);
		else if (this._updateCellsOn > 0 || this._updateCellsOnce) this._updateCells();
	}
}